Hi all,
I wanted to give some updates on current and upcoming changes to how the
results of lazy-parsing are represented in SpiderMonkey. There has been cleanup
over the past year to use more self-contained data-types (such as
js::PrivateScriptData) and reducing the amount of manual GC bookkeeping (Bug
1535138). More recently we have been moving towards using a single instance for
both the LazyScript and JSScript types (Bug 1529456).
Recent Changes:
- Various JIT-related fields were moved from JSScript into a new js::JitScript
data type (Bug 1551796). This reduced the size of JSScript and made
lifetimes easier to reason about.
- The flags for both JSScript and js::LazyScript use the same set of flags.
Note that not all flags are fully initialized for each type and some care
must be taken.
- Both JSScript and js::LazyScript inherit from a new js::BaseScript type (Bug
1566803). This contains fields (such as 'sourceObject' and 'toStringStart')
that were duplicated.
- LazyScriptData is removed and we use js::PrivateScriptData for both lazy and
non-lazy scripts. These primarily contain a variable-length bucket of
GCCellPtrs that the script may use. These pointer fields also now belong to
the BaseScript type. Note that the contents of the PrivateScriptData changes
as a script is delazified from lazy to compiled forms.
- The 'LazyScript::enclosingLazySciptOrScope', 'JSScript::warmUpCount',
'JSScript::jitScript_' fields are combined into a tagged-union type called
'js::ScriptWarmUpData'. As a script progresses from uncompiled-lazy to
JIT-optimized forms, this field changes types. This is part of the
BaseScript.
- Many uses of the LazyScript* type in code can now use BaseScript* and use a
single path to handle accessing information about both lazy and non-lazy
scripts.
- The one remaining field differing between LazyScript and JSScript is the
shared-reference to the immutable bytecode. This is unsurprisingly only
defined for a compiled script.
Upcoming Changes (FF73):
- Bug 1600705 will avoid allocating script data for leaf-function LazyScripts.
Currently this only contains a bunch of nullptrs.
- Bug 1529456 will combine the LazyScript and JSScript objects:
- The LazyScript GC arena will be removed. All LazyScripts and JSScripts
will use same object layout and size. A never-compiled script will use
the same memory as before, but any compiled scripts will use less memory.
- During delazification, we re-use the script and attach the new bytecode,
the PrivateScriptData, and switch the warmUpData to warm-up-count mode.
- Since we only relazify leaf-scripts, there will be no script data we
need to preserve and relazification can be done in-place.
- Special care is taken so that during delazification errors that we
correctly restore the script instance to a lazy form.
- We eliminate the Debugger's lazy-script tracking and only need to worry
about the association of a DebuggerScript to a single script instance.
- Stop generating JIT-code check for cases that now behave more consistently
regardless of if script is lazy (Bug 1567157).
Future Work:
- Relazification can be expanding to non-leaf functions. This can be done by
releasing bytecode, while preserving js::Scope and other data that prevents
relazification today. This would be a new operating mode of a script between
lazy and compiled, but is straightforward with the other previous work
complete.
- This is particularly useful for the top-level script which is run once
but kept alive do to entrained scopes.
- It is also possible to restore scripts to fully lazy forms. The
PrivateScriptData from the original lazy form can be regenerated easily
from a fully compiled script.
We've come a long way since what these structures looked like in ESR60:
https://searchfox.org/mozilla-esr60/rev/02b4ae79b24aae2346b1338e2bf095a571192061/js/src/vm/JSScript.h#820-1897,1908-2159
--Ted